﻿using System;
using System.Buffers;
using System.Collections.Generic;
using System.Text;

namespace Unity.String
{
    internal static class EnumFormatter<T>
    {
        const string InvalidName = "$";

        static readonly Dictionary<T, string> Names;
        static readonly Dictionary<T, byte[]> Utf8Names;

        static EnumFormatter()
        {
            var enumNames = Enum.GetNames(typeof(T));
            var enumValues = Enum.GetValues(typeof(T));
            Names = new Dictionary<T, string>(enumNames.Length);
            Utf8Names = new Dictionary<T, byte[]>(enumNames.Length);
            for (int enumItem = 0; enumItem < enumNames.Length; enumItem++)
            {
                var currentValue = (T)enumValues.GetValue(enumItem);
                if (currentValue == null)
                {
                    throw new InvalidOperationException();
                }
                else
                {
                    if (Names.ContainsKey(currentValue))
                    {
                        Names[currentValue] = InvalidName;
                        Utf8Names[currentValue] = Array.Empty<byte>();
                    }
                    else
                    {
                        Names.Add(currentValue, enumNames[enumItem]);
                        Utf8Names.Add(currentValue, Encoding.UTF8.GetBytes(enumNames[enumItem]));
                    }
                }
            }
        }

        public static bool TryFormatUtf16(T value, Span<char> destination, out int written, ReadOnlySpan<char> _)
        {
            if (!Names.TryGetValue(value, out var buffer) || buffer == InvalidName)
            {
                buffer = value.ToString();
            }

            written = buffer?.Length ?? 0;
            return buffer.AsSpan().TryCopyTo(destination);
        }

        public static bool TryFormatUtf8(T value, Span<byte> destination, out int written, StandardFormat _)
        {
            if (!Utf8Names.TryGetValue(value, out var buffer) || buffer.Length == 0)
            {
                buffer = Encoding.UTF8.GetBytes(value.ToString() ?? throw new InvalidOperationException());
            }

            written = buffer.Length;
            return buffer.AsSpan().TryCopyTo(destination);
        }
    }
}
